/**
 * 创建一个基于浏览器 history 历史对象实现的历史对象
 * @returns 
 */
function createBrowserHistory() {
  const globalHistory = window.history // 获取原生 history 历史对象
  let state = null // 状态
  let listeners = [] // 保存监听路径变化时需要执行的函数的数组

  /**
   * 当浏览器地址变化时 会自动执行数组中所有的监听函数
   */
  function listen(listener) {
    listeners.push(listener) // 保存监听路径变化需要执行的函数
    return () => { // 返回一个取消订阅的函数
      listeners = listeners.filter(item => item !== listener) // 移除监听函数
    }
  }

  function go(n) {
    globalHistory.go(n)
  }

  function goBack() {
    globalHistory.go(-1)
  }

  function goForward() {
    globalHistory.go(1)
  }

  // 添加一个新的路径 ( 历史记录条目 ) 和状态的函数
  // 两种使用方法 push('/user', {id: 1}) 或者 push({pathname: "/user", state: {id: 1}})
  function push(pathname, nextState) {
    const action = 'PUSH' // 定义动作为 PUSH 
    if (typeof pathname === 'object') { // 兼容对象形式调用
      state = pathname.state
      pathname = pathname.pathname
    } else { // 字符串形式调用
      state = nextState
    }
    globalHistory.pushState(state, null, pathname) // 调用原生 history.pushState 方法

    // 自定义历史对象中的 location 属性
    let location = {
      pathname, // 新的路径
      state, // 新的状态
    }

    // 通知函数 执行所有的监听函数
    notify({
      action,
      location
    })
  }

  // 替换当前的历史记录条目
  // 两种使用方法 replace('/profile', {id: 1}) replace({pathname: '/profile', state: {id: 1}})
  function replace(pathname, nextState) {
    const action = 'REPLACE' // 定义动作为 REPLACE 
    if (typeof pathname === 'object') { // 兼容对象形式调用
      state = pathname.state
      pathname = pathname.pathname
    } else { // 字符串形式调用
      state = nextState
    }
    globalHistory.replaceState(state, null, pathname) // 调用原生 history.pushState 方法

    // 自定义历史对象中的 location 属性
    let location = {
      pathname, // 新的路径
      state, // 新的状态
    }

    // 通知函数 执行所有的监听函数
    notify({
      action,
      location
    })
  }

  // 当用户点击浏览器的前进 后退 或者调用 history.go history.back history.forward 方法
  window.addEventListener('popstate', () => {
    // 获取最新的路径信息
    let location = {
      state: globalHistory.state, // 状态从自定义历史对象中获取
      pathname: window.location.pathname, // 路径是从 location 上取值
    }
    // 通知函数 执行所有的监听函数
    notify({
      action: 'POP',
      location,
    })
  })

  // 修改自定义的历史对象中的 action 和 location 属性 并执行所有的监听函数
  function notify({ action, location }) {
    history.action = action
    history.location = location
    history.length = globalHistory.length
    // 依次执行所有的监听函数
    listeners.forEach(listener => {
      listener({ action, location }) // 执行监听函数的时候传递 { location }
    })
  }


  // 初始化自定义的历史对象
  const history = {
    action: 'POP', // 动作
    go,
    goBack,
    goForward,
    push,
    replace,
    listen,
    location: {
      pathname: window.location.pathname, // 路径
      state: globalHistory.state, // 路径对应的状态 可能没有 调用了原生 pushState 将传递状态才有
    }
  }

  return history
}

export default createBrowserHistory
